﻿using System;
using System.Collections.Generic;
using System.Text;
using Rage.Base.Abilities;

namespace Rage.Marauder {
    public class CharacterCalculationsMarauder : CharacterCalculationsBase {
        #region Variables
        public Base.StatsWarrior AverageStats { get; set; }
        public Base.StatsWarrior MaximumStats { get; set; }
        public Base.StatsWarrior UnbuffedStats { get; set; }
        public Base.StatsWarrior BuffedStats { get; set; }
        public Base.StatsWarrior BuffsStats { get; set; } // The actual stats that come from Buffs
        public CombatFactorsMarauder CombatFactors { get; set; }
        public Rotation Rot { get; set; }
        public Stats BasicStats { get; set; }
        //public AbilityModelList Abilities { get; set; }

        #endregion

        #region Points
        private float _overallPoints = 0f;
        public override float OverallPoints { get { return _overallPoints; } set { _overallPoints = value; } }
        private float[] _subPoints = new float[] { 0f, 0f };
        public override float[] SubPoints { get { return _subPoints; } set { _subPoints = value; } }
        public float TotalDPS { get { return _subPoints[0]; } set { _subPoints[0] = value; } }
        public float TotalHPS;
        public float Survivability { get { return _subPoints[1]; } set { _subPoints[1] = value; } }
        #endregion

        #region Display Values
        public int TargetLevel { get; set; }
        public float Duration { get; set; }
        public bool PTRMode { get; set; }
        #region Attack Table
        public float Miss { get; set; }
        public float AccuracyRating { get; set; }
        public float AgilityCritBonus { get; set; }
        public float CritRating { get; set; }
        public float CritPercent { get; set; }
        public float MHCrit { get; set; }
        public float OHCrit { get; set; }
        #endregion
        #region Offensive
        public float ArmorPenetration { get; set; }
        public float AlacrityRating { get; set; }
        public float AlacrityPercent { get; set; }
        #endregion
        #region DPS
        // Special Damage (Shadow, Fire, etc)
        public float SpecProcDPS { get; set; }
        public float SpecProcDmgPerHit { get; set; }
        public float SpecProcActs { get; set; }
        //
        public float TotalDamagePerSecond { get; set; }
        #endregion
        #region Abilities
        // Markov Work
        //public Skills.FakeWhite FW { get; set; }
        #endregion
        #region Neutral
        public float BaseHealth { get; set; }
        public float WhiteRageO20 { get; set; }
        public float OtherRageO20 { get; set; }
        public float NeedyRageO20 { get; set; }
        public float FreeRageO20 { get; set; }
        public float WhiteRageU20 { get; set; }
        public float OtherRageU20 { get; set; }
        public float NeedyRageU20 { get; set; }
        public float FreeRageU20 { get; set; }
        public float Stamina { get; set; }
        public float Health { get; set; }
        #endregion
        #region Defensive
        public float Armor { get; set; }
        public float CritReduction { get; set; }
        public float ArmorReduction { get; set; }
        public float GuaranteedReduction { get; set; }
        public float DamageReduc { get; set; }
        public float MissedAttacks { get; set; }
        public float AvoidedAttacks { get; set; }
        public float DodgedAttacks { get; set; }
        public float ParriedAttacks { get; set; }
        public float BlockedAttacks { get; set; }
        #endregion
        #endregion

        private static string GenFormattedString(string[] passiveContrs) { return GenFormattedString(passiveContrs, false, false, false); }
        private static string GenFormattedString(string[] passiveContrs, bool hasCap, bool unprocisOverCap, bool procisOverCap)
        {
            int formIter = 2;
            string theFormat = "";

            theFormat += "{0:000.00%} : {1:0000.0}*"; // Averaged % and Averaged Rating
            theFormat += "The Pane shows Averaged Values*"; // The Header for the Tooltip
            //theFormat += "\r\n";
            if (passiveContrs.Length > 0) {
                theFormat += "\r\n= Your Passive Contributions =";
                foreach (string passiveContr in passiveContrs) {
                    theFormat += "\r\n{" + string.Format("{0}", formIter) + ":000.00%} : " + passiveContr;
                    formIter++;
                }
                theFormat += "\r\n";
            }
            theFormat += "\r\n= UnProc'd =";
            theFormat += "\r\n{" + string.Format("{0}", formIter) + ":0.#} : Rating"; formIter++;
            theFormat += "\r\n{" + string.Format("{0}", formIter) + ":000.00%} : Percent"; formIter++;
            if (hasCap) { theFormat += "\r\n" + (unprocisOverCap ? "{" + string.Format("{0}", formIter) + ":0.#} Rating Over Cap"
                                                                 : "{" + string.Format("{0}", formIter) + ":0.#} Rating Under Cap"); formIter++; }
            theFormat += "\r\n";
            theFormat += "\r\n= Proc'd =";
            theFormat += "\r\n{" + string.Format("{0}", formIter) + ":0.#} : Rating"; formIter++;
            theFormat += "\r\n{" + string.Format("{0}", formIter) + ":000.00%} : Percent"; formIter++;
            if (hasCap) { theFormat += "\r\n" + (procisOverCap ? "{" + string.Format("{0}", formIter) + ":0.#} Rating Over Cap"
                                                               : "{" + string.Format("{0}", formIter) + ":0.#} Rating Under Cap"); formIter++; }

            return theFormat;
        }
        private static string GenFormattedString(string[] passiveContrs, bool hasCap,
            bool whunprocisOverCap, bool whprocisOverCap,
            bool ywunprocisOverCap, bool ywprocisOverCap)
        {
            int formIter = 2;
            string theFormat = "";

            theFormat += "{0:000.00%} : {1}*"; // Averaged % and Averaged Rating
            theFormat += "The Pane shows Averaged Values*"; // The Header for the Tooltip
            //theFormat += "\r\n";
            if (passiveContrs.Length > 0) {
                theFormat += "\r\n= Your Passive Contributions =";
                foreach (string passiveContr in passiveContrs) {
                    theFormat += "\r\n{" + string.Format("{0}", formIter) + ":000.00%} : " + passiveContr;
                    formIter++;
                }
                theFormat += "\r\n";
            }
            theFormat += "\r\n= UnProc'd =";
            theFormat += "\r\n{" + string.Format("{0}", formIter) + ":0.#} : Rating"; formIter++;
            theFormat += "\r\n{" + string.Format("{0}", formIter) + ":000.00%} : Percent"; formIter++;
            if (hasCap) {
                theFormat += "\r\n{" + string.Format("{0}", formIter) + ":0.#} Rating "
                                     + (whunprocisOverCap ? "Over White Cap" : "Under White Cap");
                formIter++;
            }
            theFormat += "\r\n{" + string.Format("{0}", formIter) + ":000.00%} : Percent"; formIter++;
            if (hasCap) {
                theFormat += "\r\n{" + string.Format("{0}", formIter) + ":0.#} Rating "
                                     + (ywunprocisOverCap ? "Over Yellow Cap" : "Under Yellow Cap");
                formIter++;
            }
            theFormat += "\r\n";
            theFormat += "\r\n= Proc'd =";
            theFormat += "\r\n{" + string.Format("{0}", formIter) + ":0.#} : Rating"; formIter++;
            theFormat += "\r\n{" + string.Format("{0}", formIter) + ":000.00%} : Percent"; formIter++;
            if (hasCap) {
                theFormat += "\r\n{" + string.Format("{0}", formIter) + ":0.#} Rating "
                + (whprocisOverCap ? "Over White Cap" : "Under White Cap");
                formIter++;
            }
            theFormat += "\r\n{" + string.Format("{0}", formIter) + ":000.00%} : Percent"; formIter++;
            if (hasCap) {
                theFormat += "\r\n{" + string.Format("{0}", formIter) + ":0.#} Rating "
                    + (ywprocisOverCap ? "Over Yellow Cap" : "Under Yellow Cap");
                formIter++;
            }

            return theFormat;
        }

        public override Dictionary<string, string> GetCharacterDisplayCalculationValues()
        {
            Dictionary<string, string> dictValues = new Dictionary<string, string>();
            try {
                string format = "";
                int LevelDif = CombatFactors.LevelDif;

                // Base Stats
                #region Health & Stamina
                dictValues.Add("Health and Stamina", string.Format("{0:##,##0} : {1:##,##0}*" +
                                    "{2:00,000} : Base Health" +
                                    "\r\n{3:00,000} : Stam Bonus",
                                    AverageStats.Health, AverageStats.Endurance,
                                    BaseHealth,
                                    StatConversion.GetHealthFromEndurance(AverageStats.Endurance)));
                #endregion
                dictValues.Add("Armor", string.Format("{0}", Armor));
                #region Strength
                {
                    int formIter = 1;
                    string theFormat = "";

                    float[] passiveContrsVals = new float[] {
                        BuffsStats.Strength,
                        BuffsStats.BonusStrengthMultiplier,
                    };

                    string[] passiveContrs = new string[] {
                        "Buffs : Simple",
                        "Buffs : Multi",
                    };

                    theFormat += "{0:0.#}*"; // Averaged % and Averaged Rating
                    theFormat += "The Pane shows Averaged Values*";
                    //theFormat += "\r\n";
                    theFormat += "\r\n= Your Passive Contributions =";
                    theFormat += "\r\n{" + string.Format("{0}", formIter) + ":0.#} : " + passiveContrs[0]; formIter++;
                    theFormat += "\r\n{" + string.Format("{0}", formIter) + ":00.#%} : " + passiveContrs[1]; formIter++;
                    theFormat += "\r\n";
                    theFormat += "\r\n= UnProc'd =";
                    theFormat += "\r\nValue: {" + string.Format("{0}", formIter) + ":0.#}"; formIter++;
                    theFormat += "\r\nIncreases Attack Power by {" + string.Format("{0}", formIter) + ":0.#}"; formIter++;
                    theFormat += "\r\n";
                    theFormat += "\r\n= Proc'd =";
                    theFormat += "\r\nValue: {" + string.Format("{0}", formIter) + ":0.#}"; formIter++;
                    theFormat += "\r\nIncreases Attack Power by {" + string.Format("{0}", formIter) + ":0.#}"; formIter++;

                    dictValues.Add("Strength", string.Format(theFormat,
                        // Averaged Stats
                        AverageStats.Strength,
                        // Passive Contributions
                        passiveContrsVals[0], passiveContrsVals[1], passiveContrsVals[2],
                        // UnProc'd Stats
                        BuffedStats.Strength,
                        BuffedStats.Strength * 2f,
                        // Proc'd Stats
                        MaximumStats.Strength,
                        MaximumStats.Strength * 2f
                        ));
                }
                #endregion
                #region Attack Power
                dictValues.Add("Attack Power", string.Format("{0}*" +
                                    "Increases White DPS by {1:0.0}\r\n" +
                                    "\r\n" +
                                    "Buffed: {2:0}\r\n" +
                                    "Proc'd: {3:0}", (int)AverageStats.Power, AverageStats.Power / 14f,
                                    BuffedStats.Power, MaximumStats.Power));
                #endregion
                #region Crit
                {
                    // sub to add neg number as pos, for overcapping to compensate
                    // for boss level on yellows (or whites, I dont remember which)
                    // Whites clip crit cap with glances, dodges, parries, misses
                    float useRamp = 0f;
                    if (CombatFactors.Char.MarauderTalents.Annihilate > 0f) { useRamp = 0.05f + 0.02f; }
                    float[] passiveContrsVals = new float[] {
                        0.03192f,
                        AgilityCritBonus,
                        StatConversion.GetCritFromRating(BuffedStats.CritRating),
                        0/*BuffsStats.PhysicalCrit + useRamp*/,
                    };
                    float passiveContrsTtlVal = passiveContrsVals[0] + passiveContrsVals[1]
                                              + passiveContrsVals[2] + passiveContrsVals[3];
                    string[] passiveContrs = new string[] { "Base Crit", "From Agility", "From Crit Rating", "Buffs" };


                    //float YwUnProcdCrit = StatConversion.GetCritFromRating(BuffedStats.CritRating + BuffedStats.DeathbringerProc);
                    //float YwProcdCrit = StatConversion.GetCritFromRating(MaximumStats.CritRating + MaximumStats.DeathbringerProc);
                    //bool isYwUnProcdOverCap = passiveContrsTtlVal + YwUnProcdCrit > YwCritCap;
                    //bool isYwProcdOverCap = passiveContrsTtlVal + YwProcdCrit > YwCritCap;
                    //float amountYwUnProcdOverCap = Math.Abs(StatConversion.GetRatingFromCrit(YwCritCap - (passiveContrsTtlVal + YwUnProcdCrit)));
                    //float amountYwProcdOverCap = Math.Abs(StatConversion.GetRatingFromCrit(YwCritCap - (passiveContrsTtlVal + YwProcdCrit)));

                    string theFormat = GenFormattedString(passiveContrs, true,
                        false /*isWhUnProcdOverCap*/, false //isWhProcdOverCap
                        //isYwUnProcdOverCap, isYwProcdOverCap
                    );

                    dictValues.Add("Crit", string.Format(theFormat,
                        // Averaged Stats
                        CritPercent, AverageStats.CritRating,
                        // Passive Contributions
                        passiveContrsVals[0], passiveContrsVals[1],
                        passiveContrsVals[2], passiveContrsVals[3],
                        // UnProc'd Stats
                        BuffedStats.CritRating,
                        Math.Min(0, passiveContrsTtlVal), 0,
                        //Math.Min(YwCritCap, passiveContrsTtlVal + YwUnProcdCrit), amountYwUnProcdOverCap,
                        // Proc'd Stats
                        MaximumStats.CritRating,
                        Math.Min(0, passiveContrsTtlVal + 0), 0
                        //Math.Min(YwCritCap, passiveContrsTtlVal + YwProcdCrit), amountYwProcdOverCap
                        ));
                }
                #endregion
                #region Armor Penetration
                {
                    //float ArPCap = 1.00f;
                    //float[] passiveContrsVals = new float[] { 1.00f };
                    //float passiveContrsTtlVal = passiveContrsVals[0];
                    //string[] passiveContrs = new string[] { "Colossus Smash (6 sec/20 sec). Sudden Death resets cooldown sooner." };
                    //float UnProcdArP = StatConversion.GetArmorPenetrationFromRating(BuffedStats.ArmorPenetrationRating);
                    //float ProcdArP = StatConversion.GetArmorPenetrationFromRating(MaximumStats.ArmorPenetrationRating);
                    //bool isUnProcdOverCap = passiveContrsTtlVal + UnProcdArP > ArPCap;
                    //bool isProcdOverCap = passiveContrsTtlVal + ProcdArP > ArPCap;
                    //float amountUnProcdOverCap = Math.Abs(StatConversion.GetRatingFromArmorPenetration(ArPCap - (passiveContrsTtlVal + UnProcdArP)));
                    //float amountProcdOverCap = Math.Abs(StatConversion.GetRatingFromArmorPenetration(ArPCap - (passiveContrsTtlVal + ProcdArP)));
                    //string theFormat = GenFormattedString(passiveContrs, true, isUnProcdOverCap, isProcdOverCap);
                    dictValues.Add("Armor Penetration", string.Format("{0:0%}",//theFormat,
                        // Averaged Stats
                        ArmorPenetration + AverageStats.ArmorPenetration//, AverageStats.ArmorPenetrationRating,
                        // Passive Contributions
                        //passiveContrsVals[0], 
                        // UnProc'd Stats
                        //BuffedStats.ArmorPenetrationRating,
                        //Math.Min(ArPCap, passiveContrsTtlVal + UnProcdArP),
                        //amountUnProcdOverCap,
                        // Proc'd Stats
                        //MaximumStats.ArmorPenetrationRating,
                        //Math.Min(ArPCap, passiveContrsTtlVal + ProcdArP),
                        //amountProcdOverCap
                        ));
                }
                #endregion
                #region Haste
                {
                    float[] passiveContrsVals = new float[] {
                        CombatFactors.Char.MarauderTalents.Annihilate * 0.025f,
                        BuffsStats.Alacrity,
                    };
                    float passiveContrsTtlVal = (1f + passiveContrsVals[0])
                                              * (1f + passiveContrsVals[1])
                                              - 1f;
                    string[] passiveContrs = new string[] { "Blood Frenzy", "Buffs", };
                    float UnProcdHaste = StatConversion.GetAlacrityModifierFromRating(BuffedStats.AlacrityRating);
                    float ProcdHaste = StatConversion.GetAlacrityModifierFromRating(MaximumStats.AlacrityRating);
                    string theFormat = GenFormattedString(passiveContrs);

                    dictValues.Add("Haste", string.Format(theFormat,
                        // Averaged Stats
                        AlacrityPercent, AverageStats.AlacrityRating,
                        // Passive Contributions
                        passiveContrsVals[0], passiveContrsVals[1], passiveContrsVals[2],
                        // UnProc'd Stats
                        BuffedStats.AlacrityRating,
                        (1f + passiveContrsTtlVal) * (1f + UnProcdHaste) - 1f,
                        // Proc'd Stats
                        MaximumStats.AlacrityRating,
                        (1f + passiveContrsTtlVal) * (1f + ProcdHaste) - 1f
                        ));
                }
                #endregion
                #region Hit
                {
                    // old
                    float HitPercent = StatConversion.GetAccuracyFromRating(AccuracyRating);
                    float HitPercBonus = 0f;//AverageStats.PhysicalHit;
                    // Hit Soft Cap ratings check, how far from it
                    float capA1 = StatConversion.BASIC_MISS_CHANCE_CAP[LevelDif];
                    float convcapA1 = 0f; //(float)Math.Ceiling(StatConversion.GetRatingFromBasicAccuracy(capA1));
                    float sec2lastNumA1 = 0f;// (convcapA1 - StatConversion.GetRatingFromBasicAccuracy(HitPercent) - StatConversion.GetRatingFromBasicAccuracy(HitPercBonus)) * -1;
                    //float lastNumA1    = StatConversion.GetRatingFromExpertise((convcapA1 - Math.Min(MhExpertise, (OhExpertise != 0 ? OhExpertise : MhExpertise))) * -1);
                    // Hit Hard Cap ratings check, how far from it
                    float capA2 = StatConversion.BASIC_MISS_CHANCE_CAP[LevelDif];
                    float convcapA2 = 0f; //(float)Math.Ceiling(StatConversion.GetRatingFromBasicAccuracy(capA2));
                    float sec2lastNumA2 = 0f; // (convcapA2 - StatConversion.GetRatingFromBasicAccuracy(HitPercent) - StatConversion.GetRatingFromBasicAccuracy(HitPercBonus)) * -1;
                    //float lastNumA2   = StatConversion.GetRatingFromExpertise((sec2lastNumA2 - Math.Min(MhExpertise, (OhExpertise != 0 ? OhExpertise : MhExpertise))) * -1);
                    dictValues.Add("Hit",
                        string.Format("{0:000.00%} : {1}*" + "{2:0.00%} : From Other Bonuses" +
                                        "\r\n{3:0.00%} : Total Hit % Bonus" +
                                        "\r\n\r\nWhite Two-Hander Cap: " +
                                        (sec2lastNumA1 > 0 ? "You can free {4:0} Rating"
                                                           : "You need {4:0} more Rating") +
                                        "\r\nWhite Dual Wield Cap: " +
                                        (sec2lastNumA2 > 0 ? "You can free {5:0} Rating"
                                                           : "You need {5:0} more Rating"),
                                        StatConversion.GetAccuracyFromRating(AverageStats.AccuracyRating),
                                        AverageStats.AccuracyRating,
                                        HitPercBonus,
                                        HitPercent + HitPercBonus,
                                        (sec2lastNumA1 > 0 ? sec2lastNumA1 : sec2lastNumA1 * -1),
                                        (sec2lastNumA2 > 0 ? sec2lastNumA2 : sec2lastNumA2 * -1)
                                    ));
                }
                #endregion

                dictValues.Add("Description 1", string.Format("DPS  : PerHit  : #ActsD"));
                dictValues.Add("Description 2", string.Format("DPS  : PerHit  : #ActsD"));
                dictValues.Add("Description 3", string.Format("DPS  : PerHit  : #ActsD"));
                // DPS Abilities
                format = "{0:0000} : {1:00000} : {2:000.00}";
                if (TotalDPS < 0f) { TotalDPS = 0f; }
                foreach (AbilityWrapper aw in Rot.TheAbilityList)
                {
                    dictValues.Add(aw.Ability.Name, string.Format(format, aw.AllDPS, aw.Ability.DamageOnUse, aw.AllNumActivates)
                        + aw.Ability.GenTooltip(aw.AllNumActivates, aw.DPS_O20, aw.DPS_U20, aw.AllDPS / TotalDPS));
                }
                // DPS General
                dictValues.Add("Special DMG Procs", string.Format("{0:0000} : {1:00000} : {2:000.00}*{3:00.0%} of DPS",
                    SpecProcDPS, SpecProcDmgPerHit, SpecProcActs,
                    SpecProcDPS <= 0f || TotalDPS <= 0f ? 0f : SpecProcDPS / TotalDPS));
                dictValues.Add("Total DPS", string.Format("{0:#,##0} : {1:#,###,##0}*" + ((!string.IsNullOrEmpty(Rot.GCDUsage) ? Rot.GCDUsage : "No GCD Usage*")), TotalDPS, TotalDPS * Duration));
                // Rage
                format = "{0:0000}";
                dictValues.Add("Description 4", string.Format("Gen'd: Need : Avail"));
                dictValues.Add("Rage Above 20%", string.Format("{0:0000} : {1:0000} : {2:0000}", WhiteRageO20 + OtherRageO20, NeedyRageO20, FreeRageO20));
                dictValues.Add("Rage Below 20%", string.Format("{0:0000} : {1:0000} : {2:0000}", WhiteRageU20 + OtherRageU20, NeedyRageU20, FreeRageU20));
            } catch (Exception ex) {
                new Base.ErrorBox()
                {
                    Title = "Error in creating Stat Pane Dictionaries",
                    Function = "GetCharacterDisplayCalculationValues()",
                    TheException = ex,
                }.Show();
            }
            return dictValues;
        }

        public override float GetOptimizableCalculationValue(string calculation) {
            switch (calculation) {
                case "Health": return AverageStats.Health;
                case "Armor": return AverageStats.Armor;
                case "Strength": return AverageStats.Strength;
                case "Power": return AverageStats.Power;
                //case "Crit %": return CombatFactors.CMHycrit * 100f;
                //case "Haste %": return CombatFactors.TotalHaste * 100f;
                //case "ArP %": return AverageStats.ArmorPenetration * 100f;
                //case "% Chance to Miss (Yellow)": return CombatFactors.CYmiss * 100f;
                //case "% Chance to be Dodged": return CombatFactors.CMHdodge * 100f;
                //case "% Chance to be Parried": return CombatFactors.CMHparry * 100f;
                //case "% Chance to be Avoided (Yellow/Dodge)": return CombatFactors.CYmiss * 100f + CombatFactors.CMHdodge * 100f;
            }
            return 0.0f;
        }
    }
}
